#include <linux/bug.h>			/* For BUG_ON.  */
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* Needed for pr_info() */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <asm-generic/page.h>

#define  TRUE(condition)  BUG_ON(!(condition))

/**
 * The slave DMA usage consists of following steps:
 1. Allocate a DMA slave channel
 2. Set slave and controller specific parameters
 3. Get a descriptor for transaction
 4. Submit the transaction
 5. Issue pending requests and wait for callback notification

 When the optional 'filter_fn' parameter is NULL, dma_request_channel()
 simply returns the first channel that satisfies the capability mask.
 *
 *
 */

struct struct01 {
	int num;
	int timeout;
};

/* Deals with DMA transactions */

struct pl011_sgbuf {
	struct scatterlist sg;
	char *buf;
};

bool dma_filter(struct dma_chan *chan, void *filter_param) {
	pr_info("dma_filte start \n");
	return true;
}

struct dma_chan *chan;

dma_addr_t txaddr;
dma_addr_t rxaddr;

unsigned int *txbuf;
unsigned int *rxbuf;

void mydma_async_tx_callback(void *dma_async_param) {
	int i;
	for (i = 0; i < PAGE_SIZE / sizeof(unsigned int); i++) {
		pr_info("%d", *rxbuf++);
	}
}

static int __init foo_init(void) {
	char name[] = "dma_name";
	int i;
	struct dma_async_tx_descriptor *desc_tx = NULL;
	dma_cap_mask_t mask;
	dma_cap_zero(mask);
	dma_cap_set(DMA_MEMCPY, mask);

	chan = dma_request_channel(mask, NULL, name);
	if (!chan) {
		return -1;
	}

	txbuf = dma_alloc_coherent(chan->device->dev, PAGE_SIZE, &txaddr,
			GFP_KERNEL);

	for (i = 0; i < PAGE_SIZE / sizeof(unsigned int); i++) {
		*txbuf++ = i;
	}
	rxbuf = dma_alloc_coherent(chan->device->dev, PAGE_SIZE, &rxaddr,
			GFP_KERNEL);
	desc_tx = chan->device->device_prep_dma_memcpy(chan, rxaddr, txaddr,
			PAGE_SIZE, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
	desc_tx->callback = mydma_async_tx_callback;
	desc_tx->callback_param = NULL;
	dmaengine_submit(desc_tx);
	dma_async_issue_pending(chan);
	pr_info("end");

	return 0;
}

static void __exit foo_cleanup(void) {
	dma_free_coherent(chan->device->dev, PAGE_SIZE, rxbuf, rxaddr);
	dma_free_coherent(chan->device->dev, PAGE_SIZE, txbuf, txaddr);
	dma_release_channel(chan);
}

module_init(foo_init);
module_exit(foo_cleanup);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("andy one-key driver");
MODULE_ALIAS("one-key");
